<template>
	<div class="icon-selector w100 h100">
		<el-input
			v-model="state.fontIconSearch"
			:placeholder="state.fontIconPlaceholder"
			:clearable="clearable"
			:disabled="disabled"
			:size="size"
			ref="inputWidthRef"
			class="icon-selector-input"
			@clear="onClearFontIcon"
			@focus="onIconFocus"
			@blur="onIconBlur"
		>
			<template #prepend>
				<div class="icon-selector-prepend">
					<i
						:class="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix"
						v-if="isElementPlusIcon(state.fontIconPrefix === '' ? prepend : state.fontIconPrefix)"
					></i>
					<i v-else :class="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix"></i>
				</div>
			</template>
		</el-input>
		<el-popover
			placement="bottom"
			:width="state.fontIconWidth"
			transition="el-zoom-in-top"
			popper-class="icon-selector-popper"
			trigger="click"
			:virtual-ref="inputWidthRef"
			virtual-triggering
		>
			<template #default>
				<div class="icon-selector-warp">
					<div class="icon-selector-warp-title">{{ title }}</div>
					<el-tabs v-model="state.fontIconTabActive" @tab-click="onIconClick">
						<el-tab-pane lazy label="iconfont" name="iconfont">
							<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
						</el-tab-pane>
					</el-tabs>
				</div>
			</template>
		</el-popover>
	</div>
</template>

<script setup lang="ts" name="iconSelector">
import { defineAsyncComponent, ref, reactive, onMounted, onUnmounted, nextTick, computed, watch } from 'vue';
import type { TabsPaneContext } from 'element-plus';
import initIconfont from '/@/utils/getStyleSheets';
import '/@/theme/iconSelector.scss';
import { useI18n } from 'vue-i18n';

// 图标库配置类型
interface IconLibConfig {
	name: string;
	prefix: string;
	loader: () => Promise<unknown>;
	formatter: (icons: string[]) => string[];
}

// 图标库配置映射
const iconLibConfigs: Record<string, IconLibConfig> = {
	iconfont: {
		name: 'iconfont',
		prefix: 'iconfont',
		loader: initIconfont.custom,
		formatter: (icons: string[]) => icons.map((i: string) => (i.includes('iconfont') ? i : `iconfont ${i}`)),
	},
};

// 图标类型识别映射
const iconTypeMap: Record<string, string> = {
	iconfont: 'iconfont',
};

// 定义父组件传过来的值
const props = defineProps({
	// 输入框前置内容
	prepend: {
		type: String,
		default: () => 'ele-Pointer',
	},
	// 输入框占位文本
	placeholder: {
		type: String,
		default: () => useI18n().t('message._system.iconselector.placeholder'),
	},
	// 输入框大小
	size: {
		type: String,
		default: () => 'default',
	},
	// 弹窗标题
	title: {
		type: String,
		default: () => useI18n().t('message._system.iconselector.select'),
	},
	// 禁用
	disabled: {
		type: Boolean,
		default: () => false,
	},
	// 是否可清空
	clearable: {
		type: Boolean,
		default: () => true,
	},
	// 自定义空状态描述文字
	emptyDescription: {
		type: String,
		default: () => useI18n().t('message._system.iconselector.noicon'),
	},
	// 双向绑定值
	modelValue: {
		type: String,
		default: '',
	},
});

// 定义子组件向父组件传值/事件
const emit = defineEmits(['update:modelValue', 'get', 'clear']);

// 引入组件
const IconList = defineAsyncComponent(() => import('/@/components/iconSelector/list.vue'));

// 定义变量内容
const inputWidthRef = ref<any>();
const state = reactive({
	fontIconPrefix: '',
	fontIconWidth: 0,
	fontIconSearch: '',
	fontIconPlaceholder: '',
	fontIconTabActive: 'iconfont', // 默认为iconfont
	fontIconList: {
		iconfont: [] as string[],
	},
});

// 工具函数：判断是否为Element Plus图标
const isElementPlusIcon = (iconClass: string): boolean => {
	return iconClass?.includes('ele-') || false;
};

// 工具函数：获取图标库类型
const getIconLibraryType = (iconValue: string): string => {
	if (!iconValue) return 'iconfont';

	for (const [key, type] of Object.entries(iconTypeMap)) {
		if (iconValue.includes(key)) {
			return type;
		}
	}
	return 'iconfont';
};

// 处理 input 获取焦点时，modelValue 有值时，改变 input 的 placeholder 值
const onIconFocus = (): void => {
	if (!props.modelValue) return;
	state.fontIconSearch = '';
	state.fontIconPlaceholder = props.modelValue;
};

// 处理 input 失去焦点时，为空将清空 input 值，为点击选中图标时，将取原先值
const onIconBlur = (): void => {
	const list = fontIconTabNameList();
	setTimeout(() => {
		const matchedIcon = list.find((icon: string) => icon === state.fontIconSearch);
		if (!matchedIcon) {
			state.fontIconSearch = '';
		}
	}, 300);
};

// 图标搜索及图标数据显示
const fontIconSheetsFilterList = computed(() => {
	const list = fontIconTabNameList();
	if (!state.fontIconSearch) return list;

	const search = state.fontIconSearch.trim().toLowerCase();
	return list.filter((item: string) => item && typeof item === 'string' && item.toLowerCase().includes(search));
});

// 根据 tab name 类型设置图标
const fontIconTabNameList = (): string[] => {
	return state.fontIconList[state.fontIconTabActive as keyof typeof state.fontIconList] || [];
};

// 处理 icon 双向绑定数值回显
const initModeValueEcho = (): void => {
	if (!props.modelValue) {
		state.fontIconPlaceholder = props.placeholder;
		return;
	}
	state.fontIconPlaceholder = props.modelValue;
	state.fontIconPrefix = props.modelValue;
};

// 处理 icon 类型，用于回显时，tab 高亮与初始化数据
const initFontIconName = (): string => {
	const type = getIconLibraryType(props.modelValue);
	state.fontIconTabActive = type;
	return type;
};

// 初始化数据
const initFontIconData = async (name: string): Promise<void> => {
	const config = iconLibConfigs[name];
	if (!config) {
		console.warn(`未找到图标库配置: ${name}`);
		return;
	}

	// 如果已经加载过，直接返回
	const currentList = state.fontIconList[name as keyof typeof state.fontIconList];
	if (currentList && currentList.length > 0) {
		return;
	}

	try {
		const icons = await config.loader();
		state.fontIconList[name as keyof typeof state.fontIconList] = config.formatter(icons as string[]);
	} catch (error) {
		console.error(`加载图标库 ${name} 失败:`, error);
		state.fontIconList[name as keyof typeof state.fontIconList] = [];
	}

	// 初始化 input 的 placeholder
	state.fontIconPlaceholder = props.placeholder;
	// 初始化双向绑定回显
	initModeValueEcho();
};

// 图标点击切换
const onIconClick = (pane: TabsPaneContext): void => {
	initFontIconData(pane.paneName as string);
	inputWidthRef.value?.focus();
};

// 获取当前点击的 icon 图标
const onColClick = (iconValue: string): void => {
	state.fontIconPlaceholder = iconValue;
	state.fontIconPrefix = iconValue;
	emit('get', state.fontIconPrefix);
	emit('update:modelValue', state.fontIconPrefix);
	inputWidthRef.value?.focus();
};

// 清空当前点击的 icon 图标
const onClearFontIcon = (): void => {
	state.fontIconPrefix = '';
	emit('clear', state.fontIconPrefix);
	emit('update:modelValue', state.fontIconPrefix);
};

// 获取 input 的宽度
const getInputWidth = (): void => {
	nextTick(() => {
		if (inputWidthRef.value?.$el) {
			state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
		}
	});
};

// 监听页面宽度改变
let resizeObserver: ResizeObserver | null = null;
const initResize = (): void => {
	// 使用 ResizeObserver 替代 window resize 事件，性能更好
	if (window.ResizeObserver) {
		resizeObserver = new ResizeObserver(() => {
			getInputWidth();
		});

		if (inputWidthRef.value?.$el) {
			resizeObserver.observe(inputWidthRef.value.$el);
		}
	} else {
		// 降级方案：使用 window resize 事件
		window.addEventListener('resize', getInputWidth);
	}
};

// 清理资源
const cleanup = (): void => {
	if (resizeObserver) {
		resizeObserver.disconnect();
		resizeObserver = null;
	} else {
		window.removeEventListener('resize', getInputWidth);
	}
};

// 页面加载时
onMounted(() => {
	initFontIconData(initFontIconName());
	initResize();
	getInputWidth();
});

// 页面卸载时清理资源
onUnmounted(() => {
	cleanup();
});

// 监听双向绑定 modelValue 的变化
watch(
	() => props.modelValue,
	() => {
		initModeValueEcho();
		initFontIconName();
	}
);
</script>

<style scoped lang="scss">
.icon-selector {
	.icon-selector-input {
		:deep(.el-input__wrapper) {
			border-radius: 4px;
			border: 1px solid var(--el-border-color-light);
			transition: all 0.3s ease;
			background: var(--el-bg-color);

			&:hover {
				border-color: var(--el-color-primary);
			}

			&.is-focus {
				border-color: var(--el-color-primary);
			}
		}

		:deep(.el-input-group__prepend) {
			border-radius: 4px 0 0 4px;
			border: 1px solid var(--el-border-color-light);
			border-right: none;
			background: var(--el-fill-color-blank);
			transition: all 0.3s ease;

			.icon-selector-prepend {
				display: flex;
				align-items: center;
				justify-content: center;
				width: 32px;
				height: 100%;

				i {
					font-size: 16px;
					color: var(--el-text-color-regular);
					transition: all 0.3s ease;
				}
			}
		}

		:deep(.el-input__wrapper) {
			border-radius: 0 4px 4px 0 !important;
		}

		&:hover {
			:deep(.el-input-group__prepend) {
				border-color: var(--el-color-primary);

				.icon-selector-prepend i {
					color: var(--el-color-primary);
				}
			}
		}

		&.is-focus {
			:deep(.el-input-group__prepend) {
				border-color: var(--el-color-primary);

				.icon-selector-prepend i {
					color: var(--el-color-primary);
				}
			}
		}
	}
}
</style>
